#include "pch.h"

EXTERN_C IMAGE_DOS_HEADER __ImageBase;
#define HINST_THISCOMPONENT ((HINSTANCE)&__ImageBase)

using namespace Direct2D;

HWND g_hwnd;

DeviceContext    g_d2dc;
Dxgi::SwapChain1 g_swapChain;
Factory1         g_d2fac;

DirectWrite::Factory    g_dwrite;
DirectWrite::TextLayout g_fpsLayout;
DirectWrite::TextFormat g_textFormat;

SolidColorBrush  g_redBrush;

LRESULT _stdcall WndProc(HWND, UINT, WPARAM, LPARAM);
void CreateDeviceResources();
void CreateDeviceSizeResources();
void CreateDeviceIndependentResources();
void Render();
void Draw();
void DrawFps();

// wWinMain
int _stdcall wWinMain(HINSTANCE, HINSTANCE, PWSTR, int)
{
	WNDCLASSEX wc    = { sizeof(wc) };
	wc.lpszClassName = L"Simple Window";
	wc.lpfnWndProc   = WndProc;
	wc.hCursor       = LoadCursor(nullptr, IDC_ARROW);
	wc.hIcon         = LoadIcon(nullptr, IDI_APPLICATION);
	RegisterClassEx(&wc);

	g_hwnd = CreateWindowEx(0, 
							wc.lpszClassName, 
							L"Simple Window", 
							WS_OVERLAPPEDWINDOW|WS_VISIBLE, 
							CW_USEDEFAULT, 
							CW_USEDEFAULT, 
							CW_USEDEFAULT, 
							CW_USEDEFAULT, 
							nullptr, 
							nullptr, 
							HINST_THISCOMPONENT, 
							nullptr);

	CreateDeviceIndependentResources();

	MSG msg{ 0 };
	do
	{
		while (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE))
		{
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}

		Render();

	} while (msg.message != WM_QUIT);

	return 0;
}

// WndProc
LRESULT _stdcall WndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
	if (msg == WM_DESTROY)
	{
		PostQuitMessage(0);
	}
	else if (msg == WM_SIZE)
	{
		if (g_d2dc && wparam != SIZE_MINIMIZED)
		{
			g_d2dc.SetTarget();

			g_swapChain.ResizeBuffers();

			auto bitmap = g_d2dc.CreateBitmapFromDxgiSurface(g_swapChain.GetBuffer());
			g_d2dc.SetTarget(bitmap);

			CreateDeviceSizeResources();
		}

		InvalidateRect(hwnd, nullptr, false);
	}

	return DefWindowProc(hwnd, msg, wparam, lparam);
}

// Render
void Render()
{
	if (!g_d2dc)
	{
		auto d3d = Direct3D::CreateDevice();

		auto dxgi = d3d.GetDxgiFactory();
		g_swapChain = dxgi.CreateSwapChainForHwnd(d3d, g_hwnd);

		auto d2d = g_d2fac.CreateDevice(d3d);
		g_d2dc = d2d.CreateDeviceContext();

		auto bitmap = g_d2dc.CreateBitmapFromDxgiSurface(g_swapChain.GetBuffer());
		g_d2dc.SetTarget(bitmap);

		CreateDeviceResources();
		CreateDeviceSizeResources();
	}
	g_d2dc.BeginDraw();
	Draw();
	g_d2dc.EndDraw();

	g_swapChain.Present();
}

// Draw
void Draw()
{
	g_d2dc.Clear(ColorF(ColorF::LightSkyBlue));

	auto size = g_d2dc.GetSize();

	g_d2dc.DrawRectangle(RectF{ 50.0f, 50.0f, size.Width - 50.0f, size.Height - 50.0f }, g_redBrush, 10.0f);

	DrawFps();
}

// CreateDeviceResources
void CreateDeviceResources()
{
	g_redBrush = g_d2dc.CreateSolidColorBrush(ColorF(ColorF::OrangeRed));
}

// CreateDeviceSizeResources
void CreateDeviceSizeResources()
{

}

// CreateDeviceIndependentResources
void CreateDeviceIndependentResources()
{
	g_d2fac = CreateFactory();

	g_dwrite = DirectWrite::CreateFactory();

	g_textFormat = g_dwrite.CreateTextFormat(L"Consolas", 18.0f);
}

// DrawFps
void DrawFps()
{
	static unsigned lastTick = GetTickCount();

	static float fps = 0.0f;

	static int frames = 0;

	++frames;

	auto tickCount = GetTickCount();

	auto diff = tickCount - lastTick;

	if (diff > 1000)
	{
		fps = 1.0f * frames / diff * 1000.0f;

		frames = 0;

		lastTick = tickCount;

		WCHAR fpsText[20];
		swprintf_s(fpsText, L"FPS: %.2f", fps);
		g_fpsLayout = g_dwrite.CreateTextLayout(fpsText, wcslen(fpsText), g_textFormat, 1000.0f, 1000.0f);	
	}

	// draw fps layout
	if (g_fpsLayout)
	{
		g_d2dc.DrawTextLayout(Point2F(), g_fpsLayout, g_redBrush);
	}
}